﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Collections;
using System.Dynamic;
// using Autodesk.AutoCAD.DatabaseServices;

namespace Autodesk.AutoCAD.Windows.Features.PointCloud.PointCloudColorMapping
{
    class ColorSelector
    {
        static public Color? SelectColor(Color? input)
        {
#if !EXTERNAL
            Color? result = null;
            Windows.ColorDialog colorDlg = new Windows.ColorDialog();
            if (input != null)
                colorDlg.Color = Colors.Color.FromColor(input.GetValueOrDefault());
            Autodesk.AutoCAD.Windows.ColorDialog.ColorTabs tabs = Autodesk.AutoCAD.Windows.ColorDialog.ColorTabs.ACITab | Autodesk.AutoCAD.Windows.ColorDialog.ColorTabs.TrueColorTab;

            colorDlg.SetDialogTabs(tabs);

            if (true == colorDlg.ShowModal())
            {
                result = Color.FromRgb(colorDlg.Color.ColorValue.R, colorDlg.Color.ColorValue.G, colorDlg.Color.ColorValue.B);
            }
            return result;
#else
            return Color.FromRgb(100,0,34);
#endif
        }

    }
    public static class DepUtil
    {
        public static T getProp<T>(this DependencyObject d, string name)
        {
            var props = d.GetType().GetField("props").GetValue(d) as DependencyProperty[];
            var p = props.First(oo => oo.Name.Equals(name));
            return (T)d.GetValue(p);
        }   
        public static void setProp(this DependencyObject d, string name, object o)
        {
            
            var props = d.GetType().GetField("props").GetValue(d) as DependencyProperty[];
            var p = props.First(oo => oo.Name.Equals(name));
            if (o != null)
            {
                try
                {
                    d.SetValue(p, o);
                }
                catch (ArgumentOutOfRangeException)
                {
                	
                }
            }
            else
                d.ClearValue(p);
        }
        public static DependencyObject duplicate(this DependencyObject d)
        {
             var t = d.GetType().GetConstructor(new Type[0]).Invoke(null) as DependencyObject;
             var locals = d.GetLocalValueEnumerator();
             while (locals.MoveNext())
             {
                 var prop = locals.Current.Property;
                 if (!prop.ReadOnly) 
                 {
                     t.SetValue(prop, d.GetValue(prop)); 
                 }
             }
             return t;
        }
    }
    public class UnitUtils
    {
        public static double StringToDouble(string s)
        {
#if !EXTERNAL
            return Autodesk.AutoCAD.Runtime.Converter.StringToDistance(s);
#else
            return System.Double.Parse(s);
#endif
        }
        public static string DoubleToString(double d)
        {
#if !EXTERNAL
            return Autodesk.AutoCAD.Runtime.Converter.DistanceToString(d, (Autodesk.AutoCAD.Runtime.DistanceUnitFormat.Current), 2);
#else
            return d.ToString();
#endif
        }
    }

    public class MObject : Freezable, IDynamicMetaObjectProvider
    {
        class MObjectDynamic : System.Dynamic.DynamicMetaObject
        {
            System.Reflection.MethodInfo getPropertyValueMethodInfo = typeof(MObject).GetMethod("getProp");
            System.Reflection.MethodInfo setPropertyValueMethodInfo = typeof(MObject).GetMethod("setProp");

            public MObjectDynamic(System.Linq.Expressions.Expression parameter, MObject target)
                : base(parameter, BindingRestrictions.Empty, target)
            {
            }

            public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
            {
                System.Linq.Expressions.Expression s = System.Linq.Expressions.Expression.Convert(this.Expression, typeof(MObject));
                System.Linq.Expressions.Expression value = System.Linq.Expressions.Expression.Call(s, getPropertyValueMethodInfo, System.Linq.Expressions.Expression.Constant(binder.Name));
                return new DynamicMetaObject(value, BindingRestrictions.GetTypeRestriction(this.Expression, this.LimitType));
            }

            public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
            {
                System.Linq.Expressions.Expression s = System.Linq.Expressions.Expression.Convert(this.Expression, typeof(MObject));
                System.Linq.Expressions.Expression objectValue = System.Linq.Expressions.Expression.Convert(value.Expression, typeof(object));
                System.Linq.Expressions.Expression valueExp = System.Linq.Expressions.Expression.Call(s, setPropertyValueMethodInfo, System.Linq.Expressions.Expression.Constant(binder.Name), objectValue);
                return new DynamicMetaObject(valueExp, BindingRestrictions.GetTypeRestriction(this.Expression, this.LimitType));
            }
            public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
            {
                System.Reflection.MethodInfo minfo = typeof(MObject).GetMethod(binder.Name);
                var eargs = args.Select((a, i) => System.Linq.Expressions.Expression.Convert(a.Expression, minfo.GetParameters()[i].ParameterType)).ToArray();
                return new DynamicMetaObject(System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression.Convert(this.Expression, typeof(MObject)), minfo, eargs), BindingRestrictions.GetTypeRestriction(this.Expression, this.LimitType));
            }
        }

        public object getProp(string name)
        {
            return this.getProp<object>(name);
        }
        public object setProp(string name, object v)
        {
            DepUtil.setProp(this, name, v);
            return v;
        }
        public DynamicMetaObject GetMetaObject(System.Linq.Expressions.Expression parameter)
        {
            return new MObjectDynamic(parameter, this);
        }

        protected override Freezable CreateInstanceCore()
        {
            return new MObject();
        }
    }


    public class Ramp : MObject
    {
        public Ramp() { CreateBind(); }

        private void CreateBind()
        {
        }

        public static readonly DependencyProperty[] props =
            {
            DependencyProperty.Register("Name", typeof(string), typeof(Ramp), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("Num", typeof(int), typeof(Ramp), new FrameworkPropertyMetadata(2, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("IsGrad", typeof(bool), typeof(Ramp), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("Colors", typeof(Color[]), typeof(Ramp), new FrameworkPropertyMetadata(new Color[]{new Color(),new Color()}, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("GUID", typeof(string), typeof(Ramp), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("Removable", typeof(bool), typeof(Ramp), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            };

        public static Color[]  ReCalc(Color[] cols, int num)
        {
            var onum = cols.Length;
            var ncols = new Color[num];
            for (int i = 0; i < num ; i++)
            {
                double o = (double) (onum - 1) / (num - 1);
                Color r = cols[0];
                if (i == num -1)
                {
                    r = cols[onum - 1];
                }
                else if (i != 0)
                {
                    double f = (i) * o;
                    int x = (int)f;
                    int y = x;
                    if (x == 0)
                        y = 1;
                    else if (y == onum - 1)
                        x = onum - 2;
                    else
                        y++;
                    Color a = cols[x];
                    Color b = cols[y];
                    double p = 1-(f - x);
                    double q = 1-p;
                    r = Color.FromRgb((byte)(p * a.R + q * b.R), (byte)(p * a.G + q * b.G), (byte)(p * a.B + q * b.B));
                }
                ncols[i] = r;
            }
            return ncols;
        }
        public void Reverse()
        {
            var cols = GetValue(props[3]) as Color[];
            cols = cols.Reverse().ToArray();
            SetValue(props[3], cols);
        }
        private static void OnPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var prop = e.Property;
            if (prop == props[1])
            {
                int v = (int)e.NewValue;
                var cols = d.GetValue(props[3]) as Color[];
                if (v == cols.Length)
                    return;
                cols = ReCalc(cols, v);
                d.SetValue(props[3], cols);
            }
            else if (prop == props[3])
            {
                var cols = e.NewValue as Color[];
                d.SetValue(props[1], cols.Length);
            }
        }
        public static readonly int[] intervals = Enumerable.Range(2,24).ToArray();
        static Ramp()
        {

        }

        public IEnumerable<int> Intervals
        {
            get { return intervals; }
        }

        private static object CoerceProp(DependencyObject d, object value)
        {
            if (value is Int32)
            {
                var v = (int)value;
                if (v < 2 || v > 40)
                    throw new ArgumentException();
            }
            return value;
        }

        protected override Freezable CreateInstanceCore()
        {
            return new Ramp();
        }
    }

    public class ClassificationRamp : MObject
    {
        protected override Freezable CreateInstanceCore()
        {
            return new ClassificationRamp();
        }
        public static readonly string[] PointClassification = {
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.Createdneverclassified,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.Unclassified,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.Ground,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.LowVegetation,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.MediumVegetation,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.HighVegetation,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.Building,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.LowPointnoise,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.ModelKeypointmasspoint,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.Water,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.Reserved,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.Reserved,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.OverlapPoints,
        Autodesk.AutoCAD.Windows.Features.PointCloudColorMapping.PointCloudColorMapping.Reserved
        };

        public static string ClassificationName(int i)
        {
            if (i < PointClassification.Length)
            {
                return PointClassification[i];
            }
            else
            {
                return PointClassification[PointClassification.Length-1];
            }
        }

        public class Spec : Freezable
        {
            protected override Freezable CreateInstanceCore()
            {
                return new Spec();
            }
            public static readonly DependencyProperty[] props =
            {
                DependencyProperty.Register("Color", typeof(Color), typeof(Spec), new FrameworkPropertyMetadata(new Color(), FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
                DependencyProperty.Register("Index", typeof(int), typeof(Spec), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
                DependencyProperty.Register("Visible", typeof(bool), typeof(Spec), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
                DependencyProperty.Register("Name", typeof(string), typeof(Spec), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            };
            private static void OnPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                 var prop = e.Property;
                 foreach (var item in PointCloudMap.Selected)
                 {
                     if (item == d) continue;
                     item.setProp(e.Property.Name, e.NewValue);
                 }
            }
            private static object CoerceProp(DependencyObject d, object value)
            {
                return value;
            }
        }
        public static readonly DependencyProperty[] props =
            {
            DependencyProperty.Register("Name", typeof(string), typeof(ClassificationRamp), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("Num", typeof(FreezableCollection<Spec>), typeof(ClassificationRamp), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("GUID", typeof(string), typeof(ClassificationRamp), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("Removable", typeof(bool), typeof(ClassificationRamp), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            };
        private static void OnPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var prop = e.Property;
        }
        private static object CoerceProp(DependencyObject d, object value)
        {
            return value;
        }
    }

    public class PointCloud : MObject
    {
        protected override Freezable CreateInstanceCore()
        {
            return new PointCloud();
        }

//         public static List<PointCloudStylizationType> Ckind = new List<PointCloudStylizationType>() { PointCloudStylizationType.IntensityRamp, PointCloudStylizationType.HeightRamp, PointCloudStylizationType.ClassificationRamp };
        public static readonly DependencyProperty[] props =
            {
            DependencyProperty.Register("Style", typeof(int?), typeof(PointCloud), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("MinInt", typeof(int?), typeof(PointCloud), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), (a,b)=>CoerceProp(a,b,"MinInt"))),
            DependencyProperty.Register("MaxInt", typeof(int?), typeof(PointCloud), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), (a,b)=>CoerceProp(a,b,"MaxInt"))),
            DependencyProperty.Register("MinEle", typeof(double?), typeof(PointCloud), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), (a,b)=>CoerceProp(a,b,"MinEle"))),
            DependencyProperty.Register("MaxEle", typeof(double?), typeof(PointCloud), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), (a,b)=>CoerceProp(a,b,"MaxEle"))),
            DependencyProperty.Register("LimMinEle", typeof(double?), typeof(PointCloud), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("LimMaxEle", typeof(double?), typeof(PointCloud), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("OutInt", typeof(int?), typeof(PointCloud), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("OutEle", typeof(int?), typeof(PointCloud), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("LimBox", typeof(bool?), typeof(PointCloud), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("IntGrad", typeof(bool), typeof(PointCloud), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("EleGrad", typeof(bool), typeof(PointCloud), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("ShowPred", typeof(bool), typeof(PointCloud), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("Scheme", typeof(string), typeof(PointCloud), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("SupportIntensity", typeof(bool), typeof(PointCloud), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("SupportElevation", typeof(bool), typeof(PointCloud), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("SupportClassification", typeof(bool), typeof(PointCloud), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            };
        private static void OnPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var prop = e.Property;
            if (prop.Name.Equals("LimBox") && e.NewValue != null && !(bool)e.NewValue)
            {
                //d.setProp("MinEle", d.getProp<double?>("LimMinEle"));
                //d.setProp("MaxEle", d.getProp<double?>("LimMaxEle"));
            }
        }
        private static object CoerceProp(DependencyObject d, object value, string name)
        {
            if ("MaxInt" == name)
            {
                var v = d.getProp<int?>("MinInt");
                if (v != null && v.GetValueOrDefault() >= (int)value)
                    throw new FormatException("");
                if ((int)value < 0 || (int)value > 100)
                    throw new FormatException("");
            }
            if ("MinInt" == name)
            {
                var v = d.getProp<int?>("MaxInt");
                if (v != null && v.GetValueOrDefault() <= (int)value)
                    throw new FormatException("");
                if ((int)value < 0 || (int)value > 99)
                    throw new FormatException("");
            }
            if ("MaxEle" == name)
            {
                var v = d.getProp<double?>("MinEle");
                if (v != null && v.GetValueOrDefault() >= (double)value)
                    throw new FormatException("");
            }
            if ("MinEle" == name)
            {
                var v = d.getProp<double?>("MaxEle");
                if (v != null && v.GetValueOrDefault() <= (double)value)
                    throw new FormatException("");
            }
            return value;
        }
        private static object CoerceProp(DependencyObject d, object value)
        {
            return value;
        }
    }
    public class PointCloudMap : MObject 
    {
        static public IList<ClassificationRamp.Spec> Selected = new List<ClassificationRamp.Spec>();
        protected override Freezable CreateInstanceCore()
        {
            return new PointCloudMap();
        }
        public PointCloudMap()
        {
            this.setProp("Current", Current);
            this.setProp("Save", AutoSave);
            this.setProp("ShowOnly", ShowOnly);
        }

        private static bool AutoSave = true;
        private static bool ShowOnly = true;
        private static int Current = 0;
        private static Ramp SavedRamp;
        private static ClassificationRamp SavedCRamp;
        private IntPtr target;
        private Dictionary<string, Freezable> shadow = new Dictionary<string, Freezable>();
        public static readonly DependencyProperty[] props =
            {
            DependencyProperty.Register("Save", typeof(bool), typeof(PointCloudMap), new FrameworkPropertyMetadata(AutoSave, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("Current", typeof(int), typeof(PointCloudMap), new FrameworkPropertyMetadata(Current, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("ShowOnly", typeof(bool), typeof(PointCloudMap), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("Dirty", typeof(bool), typeof(PointCloudMap), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("PC", typeof(PointCloud), typeof(PointCloudMap),new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged))),
            DependencyProperty.Register("L1", typeof(FreezableCollection<Ramp>), typeof(PointCloudMap), new FrameworkPropertyMetadata(new FreezableCollection<Ramp>(), FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("L2", typeof(FreezableCollection<ClassificationRamp>), typeof(PointCloudMap), new FrameworkPropertyMetadata(new FreezableCollection<ClassificationRamp>(), FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged), new CoerceValueCallback(CoerceProp))),
            DependencyProperty.Register("DefaultScheme3", typeof(object), typeof(PointCloudMap),new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged))),
            DependencyProperty.Register("DefaultScheme2", typeof(object), typeof(PointCloudMap),new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged))),
            DependencyProperty.Register("DefaultScheme1", typeof(object), typeof(PointCloudMap),new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnPropChanged))),
            };
        private List<DependencyObject> deletedRamps = new List<DependencyObject>();
        static PointCloudMap()
        {
        }

        private static void OnPropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var prop = e.Property;
            if (!prop.Name.Equals("Dirty"))
            {
                dynamic data = d;
                if (prop.Name.Equals("Current") && (data.PC == null || ! (bool)data.Save ))
                {}  // change tab when there no selected tab does not change state
                else if (prop.Name.Equals("Save") && !(bool)e.NewValue)
                {}  // when user does not want to apply current style, change type does not change state
                else 
                d.setProp("Dirty", true);
            }
            if (prop.Name.Equals("L1"))
            {
                var list = e.NewValue as IList;
                if (list.Count > 0)
                {
                    if (d.getProp<object>("DefaultScheme1") == null)
                        d.setProp("DefaultScheme1", list[0]);
                    if (d.getProp<object>("DefaultScheme2") == null)
                        d.setProp("DefaultScheme2", list[0]);
                }
            }
            else if (prop.Name.Equals("L2"))
            {
                var list = e.NewValue as IList;
                if (list.Count > 0)
                {
                    if (d.getProp<object>("DefaultScheme3") == null)
                        d.setProp("DefaultScheme3", list[0]);
                }
            }
        }


        private static object CoerceProp(DependencyObject d, object value)
        {
            return value;
        }

        internal void DeleteRamp(DependencyObject o)
        {
            if (o == null) return;
            var list = this.getProp<IList>("L1");
            if (!list.Contains(o))
            {
                list = this.getProp<IList>("L2");
            }
            object target = null;
            if (list.Count > 1)
            {
                IList nlist = list.Cast<MObject>().OrderBy(obj=>obj.getProp<string>("Name")).ToList();
                int i = nlist.IndexOf(o);
                if (i == 0) i++;
                else i = 0;
                target = nlist[i];
                var pc = this.getProp<PointCloud>("PC");
                if (pc != null)
                {
                    var scheme = pc.getProp<string>("Scheme");
                    if (scheme != null && !scheme.Equals(String.Empty))
                    {
                        var p = list.Cast<Freezable>().SingleOrDefault(obj => obj.getProp<string>("GUID").Equals(scheme));
                        if (p != null && p != target)
                            target = p;
                    }
                }
            }
            if (this.getProp<object>("DefaultScheme1") == o)
            {
                this.setProp("DefaultScheme1", target);
            }
            if (this.getProp<object>("DefaultScheme2") == o)
            {
                this.setProp("DefaultScheme2", target);
            }
            if (this.getProp<object>("DefaultScheme3") == o)
            {
                this.setProp("DefaultScheme3", target);
            }
            deletedRamps.Add(o);
            list.Remove(o);
        }

        public static PointCloudMap Load(IntPtr? p)
        {
#if !EXTERNAL
            return LoadDB(p);
#else
            return LoadSample();
#endif
        }

        public static void Save(PointCloudMap data)
        {
            AutoSave = data.getProp<bool>("Save");
            Current = data.getProp<int>("Current");
            ShowOnly = data.getProp<bool>("ShowOnly");
#if !EXTERNAL
            SaveDB(data);
#endif
        }

        public bool HasSchemeName(string name)
        {
            IList<Ramp> rampList = this.getProp<IList<Ramp>>("L1");

            for (int i = 0; i < rampList.Count; i++)
            {
                Ramp ramp = rampList[i];

                string schemeName = ramp.getProp<string>("Name");
                if (string.Compare(schemeName, name, true) == 0)
                    return true;
            }

            IList<ClassificationRamp> crampList = this.getProp<IList<ClassificationRamp>>("L2");

            for (int i = 0; i < crampList.Count; i++)
            {
                ClassificationRamp cramp = crampList[i];

                string schemeName = cramp.getProp<string>("Name");
                if (string.Compare(schemeName, name, true) == 0)
                    return true;
            }

            return false;
        }

        public void AddScheme(DependencyObject curColorScheme, string name, string tag)
        {
            if (curColorScheme == null)
            {
                if (tag == "DefaultScheme3")
                    curColorScheme = PointCloudMap.SavedCRamp;
                else
                    curColorScheme = PointCloudMap.SavedRamp;
            }
            if (curColorScheme == null)
                return;
            if (name == null)
            {
                name = Guid.NewGuid().ToString();
            }

            var clone = (curColorScheme as Freezable).Clone();
            clone.setProp("GUID", Guid.NewGuid().ToString());
            clone.setProp("Removable", true);
            clone.setProp("Name", name);
            var tlist = this.getProp<IList>("L1");
            if (tag == "DefaultScheme3")
                tlist = this.getProp<IList>("L2");
            tlist.Add(clone);
            this.setProp(tag, clone);
            var guid = curColorScheme.getProp<string>("GUID");
            Freezable copy = null;
            if (shadow.TryGetValue(guid,out copy) && tlist.Count > 1)
            {
                tlist.Remove(curColorScheme);
                tlist.Add(copy.Clone());
            }
            shadow[clone.getProp<string>("GUID")] = clone.Clone();
        }

        public static  PointCloudMap LoadSample()
        {
            var data = new PointCloudMap();
            var ramps = new FreezableCollection<Ramp>();
            Selected.Clear();
            for (int i = 3; i < 8; i++)
            {
                var r = new Ramp();
                r.SetValue(Ramp.props[0], i.ToString());
                var guid = Guid.NewGuid().ToString();
                r.setProp("GUID", guid);
                r.setProp("Removable",true);
                r.SetValue(Ramp.props[2], i % 2 == 0);
                var cs = new Color[i];
                for (int j = 0; j < i; j++)
                {
                    cs[j] = Color.FromRgb((byte)(j * i * 100), (byte)(j * i * 20), (byte)(j * i * 300));
                    
                }
                r.SetValue(Ramp.props[3], cs);
                ramps.Add(r);
                data.shadow[guid] = (r as Freezable).Clone();
            }
            CollectionViewSource.GetDefaultView(ramps).SortDescriptions.Add(new System.ComponentModel.SortDescription("Name", System.ComponentModel.ListSortDirection.Ascending));
            data.setProp("L1", ramps);



            var ramps2 = new FreezableCollection<ClassificationRamp>();

            for (int i = 3; i < 18; i++)
            {
                var r = new ClassificationRamp();
                r.SetValue(ClassificationRamp.props[0], i.ToString());
                var guid = Guid.NewGuid().ToString();
                r.setProp("GUID", guid);
                var cs = new FreezableCollection<ClassificationRamp.Spec>();
                for (int j = 0; j < i; j++)
                {
                    var spec = new ClassificationRamp.Spec();
                    spec.setProp("Name", j.ToString());
                    spec.setProp("Index", j);
                    spec.setProp("Color", Color.FromRgb((byte)(j * i * 100), (byte)(j * i * 20), (byte)(j * i * 300)));
                    spec.setProp("Visible", j % 2 == 0);
                    cs.Add(spec);
                }
                r.SetValue(ClassificationRamp.props[1], cs);
                ramps2.Add(r);
                data.shadow[guid] = (r as Freezable).Clone();
            }
            CollectionViewSource.GetDefaultView(ramps2).SortDescriptions.Add(new System.ComponentModel.SortDescription("Name", System.ComponentModel.ListSortDirection.Ascending));

            data.setProp("L2", ramps2);

            dynamic pc = new PointCloud();
            pc.MinEle = 1.0;
            pc.MaxEle = 10.0;
            pc.LimBox = false;
            pc.OutEle = 0;
            pc.setProp("MaxEle",1.1);

            //data.setProp("Save", true);
            data.setProp("PC", (PointCloud)pc);
            PointCloudMap.SavedRamp = ramps[0];
            PointCloudMap.SavedCRamp = ramps2[0];
            return data;
        }

#if !EXTERNAL
        Autodesk.AutoCAD.DatabaseServices.Database DB = null;
        private static PointCloudMap LoadDB(IntPtr? targetId)
        {
            // Get database and transaction manager

            var db = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices.WorkingDatabase;
            if (targetId != null)
            {
                var oid = new Autodesk.AutoCAD.DatabaseServices.ObjectId(targetId.GetValueOrDefault());
                if (!oid.IsNull)
                    db = oid.Database;
            }
            if (db == null)
                return null;

            var data = new PointCloudMap();
            data.DB = db;
            Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager;
            Selected.Clear();
            using (var transaction = tm.StartTransaction())
            {
                var colormapId = Autodesk.AutoCAD.DatabaseServices.PointCloudColorMap.GetColorMapId(db);
                if (transaction != null && colormapId != null)
                {
                    var ramps = new FreezableCollection<Ramp>();
                    var cramps = new FreezableCollection<ClassificationRamp>();
                    using (var colormap = tm.GetObject(colormapId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForRead, false) as Autodesk.AutoCAD.DatabaseServices.PointCloudColorMap)
                    {
                        var inuse = new HashSet<string>(colormap.ColorSchemeInUse());
                        var inuse2 = new HashSet<string>(colormap.ClassificationColorSchemeInUse());
                        var schemes = colormap.ColorSchemeGUIDs;
                        foreach (var scheme in schemes)
                        {
                            var r = new Ramp();
                            Autodesk.AutoCAD.DatabaseServices.PointCloudColorRamp ramp = null;
                            colormap.ColorScheme(scheme, out ramp);
                            r.setProp("GUID", scheme);
                            r.setProp("Name", ramp.Name);
                            r.setProp("Removable", !inuse.Contains(scheme));
                            var colors = new Color[ramp.NumColors];
                            for (int i = 0; i < colors.Length; i++)
                            {
                                var c = ramp.Color(i);
                                colors[i] = Color.FromRgb(c.Red, c.Green, c.Blue);
                            }
                            r.setProp("Colors",colors);
                            ramps.Add(r);
                            data.shadow[scheme] = r.Clone();
                        }
                        data.setProp("L1", ramps);
                        CollectionViewSource.GetDefaultView(ramps).SortDescriptions.Add(new System.ComponentModel.SortDescription("Name", System.ComponentModel.ListSortDirection.Ascending));
                        if (SavedRamp == null && ramps.Count > 0)
                        {
                            SavedRamp = ramps[ramps.Count - 1];
                        }

                        schemes = colormap.ClassificationSchemeGUIDs;
                        foreach (var scheme in schemes)
                        {
                            var r = new ClassificationRamp();
                            Autodesk.AutoCAD.DatabaseServices.PointCloudClassificationColorRamp ramp = null;
                            colormap.ClassificationScheme(scheme, out ramp);
                            r.setProp("GUID", scheme);
                            r.setProp("Name", ramp.Name);
                            r.setProp("Removable", !inuse2.Contains(scheme));
                            var specs = new FreezableCollection<ClassificationRamp.Spec>();
                            for (int i = 0; i < ramp.NumColors; i++)
                            {
                                var s = new ClassificationRamp.Spec();
                                s.setProp("Index", i);
                                s.setProp("Name", ClassificationRamp.ClassificationName(i));
                                var c = ramp.Color(i);
                                s.setProp("Color",Color.FromRgb(c.Red, c.Green, c.Blue));
                                s.setProp("Visible", ramp.Visibility(i));
                                specs.Add(s);
                            }
                            r.setProp("Num", specs);
                            cramps.Add(r);
                            data.shadow[scheme] = r.Clone();
                        }
                        data.setProp("L2", cramps);
                        CollectionViewSource.GetDefaultView(cramps).SortDescriptions.Add(new System.ComponentModel.SortDescription("Name", System.ComponentModel.ListSortDirection.Ascending));

                        data.setProp("DefaultScheme1", ramps.FirstOrDefault(oo => oo.getProp<string>("GUID").Equals(colormap.DefaultIntensityColorScheme)));
                        data.setProp("DefaultScheme2", ramps.FirstOrDefault(oo => oo.getProp<string>("GUID").Equals(colormap.DefaultElevationColorScheme)));
                        data.setProp("DefaultScheme3", cramps.FirstOrDefault(oo => oo.getProp<string>("GUID").Equals(colormap.DefaultClassificationColorScheme)));

                        if (SavedCRamp == null && cramps.Count > 0)
                        {
                            SavedCRamp = cramps[0];
                        }
                    }
                    if (targetId != null)
                    {
                        var oid = new Autodesk.AutoCAD.DatabaseServices.ObjectId(targetId.GetValueOrDefault());
                        if (!oid.IsNull)
                        {
                            data.target = targetId.GetValueOrDefault();
                            using (var cloud = tm.GetObject(oid, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForRead, false) as Autodesk.AutoCAD.DatabaseServices.PointCloudEx)
                            {
                                dynamic pc = new PointCloud();
                                var intensity = cloud.MinMaxIntensity;
                                pc.MinInt = intensity.Item1;
                                pc.MaxInt = intensity.Item2;
                                var elev = cloud.MinMaxElevation;
                                var limb = cloud.LimitBoxBound;
                                pc.setProp("LimMinEle", limb.Item1);
                                pc.setProp("LimMaxEle", limb.Item2);
                                if ((elev.Item1 == 0.0 && elev.Item2 == 0.0) || (elev.Item1 >= elev.Item2))
                                {
                                    pc.MaxEle = pc.LimMaxEle;
                                    pc.MinEle = pc.LimMinEle;
                                }
                                else
                                {
                                    pc.MinEle = elev.Item1;
                                    pc.MaxEle = elev.Item2;
                                }
                                pc.setProp("LimBox", cloud.ElevationApplyToFixedRange);
                                pc.setProp("OutInt", (int)cloud.IntensityOutOfRangeBehavior);
                                pc.setProp("OutEle", (int)cloud.ElevationOutOfRangeBehavior);
                                pc.setProp("IntGrad", cloud.IntensityGradient);
                                pc.setProp("EleGrad", cloud.ElevationGradient);
                                pc.setProp("Style", (int)cloud.Stylization);
                                pc.setProp("Scheme", cloud.CurrentColorScheme);
                                if (PointCloud.Ckind.Contains(cloud.Stylization) && data.getProp<bool>("Save"))
                                {
                                    int c = PointCloud.Ckind.IndexOf(cloud.Stylization);
                                    data.setProp("Current", c);
                                    if (cloud.CurrentColorScheme !=null && ! cloud.CurrentColorScheme.Equals(string.Empty))
                                        data.setProp("DefaultScheme" + (c + 1), ramps.Union<MObject>(cramps).FirstOrDefault(oo => oo.getProp<string>("GUID").Equals(cloud.CurrentColorScheme)));
                                }
                                {
                                    string sname = cloud.GetColorSchemeForStylization(PointCloudStylizationType.IntensityRamp);
                                    if (sname != string.Empty)
                                        data.setProp("DefaultScheme1", ramps.FirstOrDefault(oo => sname.Equals(oo.getProp<string>("GUID"))));
                                    sname = cloud.GetColorSchemeForStylization(PointCloudStylizationType.HeightRamp);
                                    if (sname != string.Empty)
                                        data.setProp("DefaultScheme2", ramps.FirstOrDefault(oo => sname.Equals(oo.getProp<string>("GUID"))));
                                    sname = cloud.GetColorSchemeForStylization(PointCloudStylizationType.ClassificationRamp);
                                    if (sname != string.Empty)
                                        data.setProp("DefaultScheme3", cramps.FirstOrDefault(oo => sname.Equals(oo.getProp<string>("GUID"))));

                                }
                                pc.setProp("SupportIntensity", Autodesk.AutoCAD.DatabaseServices.PointCloudPropertyState.None != cloud.HasProperty(Autodesk.AutoCAD.DatabaseServices.PointCloudProperty.Intensity));
                                pc.setProp("SupportElevation", true);
                                pc.setProp("SupportClassification", Autodesk.AutoCAD.DatabaseServices.PointCloudPropertyState.None != cloud.HasProperty(Autodesk.AutoCAD.DatabaseServices.PointCloudProperty.Classification));
                                var cur = data.getProp<int>("Current");
                                if ( ( cur == 0 && !pc.getProp<bool>("SupportIntensity") ) ||
                                     (cur == 2 && !pc.getProp<bool>("SupportClassification"))
                                    )
                                    data.setProp("Current", 1);


                                data.setProp("PC", (PointCloud)pc);
                            }
                        }
                    }
                    transaction.Commit();
                }
            }
            return data;
        }
        private static void SaveDB(PointCloudMap data)
        {

            var db = data.DB;

            Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager;
            using (var transaction = tm.StartTransaction())
            {
                var colormapId = Autodesk.AutoCAD.DatabaseServices.PointCloudColorMap.GetColorMapId(db);
                if (transaction != null && colormapId != null)
                {
                    var cramps = data.getProp<IList<ClassificationRamp>>("L2");
                    IList<Ramp> rampList = data.getProp<IList<Ramp>>("L1");
                    using (var colormap = tm.GetObject(colormapId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite, false) as Autodesk.AutoCAD.DatabaseServices.PointCloudColorMap)
                    {

                        if (rampList == null)
                            return;

                        int schemeCount = rampList.Count;
                        for (int i = 0; i < schemeCount; i++)
                        {

                            Ramp ramp = rampList[i];

                            string name = ramp.getProp<string>("Name");
                            string guid = ramp.getProp<string>("GUID");
                            int colorCount = ramp.getProp<int>("Num");

                            DatabaseServices.PointCloudColorRamp PCRamp = new DatabaseServices.PointCloudColorRamp();
                            PCRamp.NumColors = colorCount;
                            PCRamp.Name = name;
                            Color[] colors = ramp.getProp<Color[]>("Colors");

                            for (int j = 0; j < colorCount; j++)
                            {
                                Color color = colors[j];
                                if (color == null)
                                    continue;

                                Colors.EntityColor Entitycolor = new Colors.EntityColor(color.R, color.G, color.B);
                                PCRamp.SetColor(j, Entitycolor);
                            }

                            colormap.SetColorScheme(guid, PCRamp);
                        }

                        if (cramps == null)
                            return;

                        foreach (var r in cramps)
                        {
                            var specs = r.getProp<IList<ClassificationRamp.Spec>>("Num");
                            var ramp = new DatabaseServices.PointCloudClassificationColorRamp();
                            ramp.Name = r.getProp<string>("Name");
                            for (int i = 0; i < specs.Count ; i++)
                            {
                                var c = specs[i].getProp<Color>("Color");
                                var Entitycolor = new Colors.EntityColor(c.R, c.G, c.B);
                                ramp.SetColor(i, Entitycolor);
                                ramp.SetVisibility(i, specs[i].getProp<bool>("Visible"));
                            }
                            colormap.SetClassificationScheme(r.getProp<string>("GUID"), ramp);
                        }
                        foreach (var d in data.deletedRamps)
                        {
                            string guid = d.getProp<string>("GUID");
                            if (d is Ramp)
                                colormap.DeleteColorScheme(guid);
                            else
                            colormap.DeleteClassificationScheme(guid);
                        }

                        // these properties must be set after L1, L2, L3
                        if (rampList.Count > 0)
                        {
                            colormap.DefaultIntensityColorScheme = data.getProp<MObject>("DefaultScheme1").getProp<string>("GUID");
                            colormap.DefaultElevationColorScheme = data.getProp<MObject>("DefaultScheme2").getProp<string>("GUID");
                        }
                        if (cramps.Count > 0)
                        {
                            colormap.DefaultClassificationColorScheme = data.getProp<MObject>("DefaultScheme3").getProp<string>("GUID");
                        }
                    }
                    var pc = data.getProp<PointCloud>("PC");
                    if (pc != null)
                    {
                        var oid = new Autodesk.AutoCAD.DatabaseServices.ObjectId(data.target);
                        using (var cloud = tm.GetObject(oid, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite, false) as Autodesk.AutoCAD.DatabaseServices.PointCloudEx)
                        {
                            cloud.MinMaxIntensity = new Tuple<int, int>(pc.getProp<int>("MinInt"), pc.getProp<int>("MaxInt"));
                            cloud.IntensityOutOfRangeBehavior = (Autodesk.AutoCAD.DatabaseServices.PointCloudDispOptionOutOfRange)pc.getProp<int>("OutInt");
                            cloud.ElevationApplyToFixedRange = pc.getProp<bool>("LimBox");
                            if (cloud.ElevationApplyToFixedRange)
                            {
                                cloud.MinMaxElevation = new Tuple<double,double>(pc.getProp<double>("MinEle"), pc.getProp<double>("MaxEle"));
                            }
                            cloud.ElevationOutOfRangeBehavior = (Autodesk.AutoCAD.DatabaseServices.PointCloudDispOptionOutOfRange)pc.getProp<int>("OutEle");
                            cloud.IntensityGradient = pc.getProp<bool>("IntGrad");
                            cloud.ElevationGradient = pc.getProp<bool>("EleGrad");
                            int c = data.getProp<int>("Current");
                            if (data.getProp<bool>("Save"))
                            {
                                cloud.Stylization = PointCloud.Ckind[c];
                            }
                            var d = data.getProp<MObject>("DefaultScheme" + (c + 1));
                            if (d != null)
                            {
                                var g = d.getProp<string>("GUID");
                                if (cloud.Stylization == PointCloud.Ckind[c])    //check the stylization sets successfully
                                    cloud.CurrentColorScheme = g;
                            }
                            if (rampList.Count > 0)
                            {
                                if (pc.getProp<bool>("SupportIntensity"))
                                    cloud.SetColorSchemeForStylization(data.getProp<MObject>("DefaultScheme1").getProp<string>("GUID"), PointCloudStylizationType.IntensityRamp);
                                if (pc.getProp<bool>("SupportElevation"))
                                    cloud.SetColorSchemeForStylization(data.getProp<MObject>("DefaultScheme2").getProp<string>("GUID"), PointCloudStylizationType.HeightRamp);
                            }
                            if (cramps.Count > 0 && pc.getProp<bool>("SupportClassification"))
                            {
                                cloud.SetColorSchemeForStylization(data.getProp<MObject>("DefaultScheme3").getProp<string>("GUID"), PointCloudStylizationType.ClassificationRamp);
                            }
                        }
                    }
                    transaction.Commit();
                }
            }
        }
#endif
    }

}
